Permutation Problem
The permutation problem involves finding all possible arrangements of elements from a given set using a backtracking algorithm. This problem can be approached considering either arrays without duplicate elements or arrays that include duplicates.
Permutations without Equal Elements
Description
Given an integer array without duplicate elements, the task is to return all possible permutations.
Backtracking Process
The permutation generation is visualized as a series of choices:
- Choices and State: The
choicesarray contains all elements of the input array, and thestatearray contains elements selected so far. - Recursive Tree Representation: Starting with an empty
state, each recursive call represents a choice made, progressing until all elements are chosen.
Pruning Repeated Choices
- Boolean Array (
selected): Utilized to track if an element fromchoiceshas been selected. - Pruning: Skip over already selected elements to reduce redundancy and exploration space.
Example: Recursive Tree and Pruning
- Initial Choices: [1, 2, 3]
- Process: Select 1, then 2, then 3 to form a permutation [1, 2, 3].
- Backtracking: Reverse selections step-by-step to explore other permutations.
def backtrack(state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]):
if len(state) == len(choices):
res.append(list(state))
return
for i, choice in enumerate(choices):
if not selected[i]:
selected[i] = True
state.append(choice)
backtrack(state, choices, selected, res)
selected[i] = False
state.pop()
def permutations_i(nums: list[int]) -> list[list[int]]:
res = []
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
return res
Permutations with Equal Elements
Description
Generate unique permutations from an array that may contain duplicates.
Handling Duplicates
- Marking: Differentiate identical elements by marking them differently.
- Hash Set (
duplicated): Used to track elements tried in the current recursive call to prevent generating duplicate permutations.
Example: Eliminating Duplicates
- Input: [1, 1, 2]
- Process: Choose first 1, then 2, marking the next identical 1 differently to prevent duplicate permutations.
def backtrack(state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]):
duplicated = set[int]()
if len(state) == len(choices):
res.append(list(state))
return
for i, choice in enumerate(choices):
if not selected[i] and choice not in duplicated:
duplicated.add(choice)
selected[i] = True
state.append(choice)
backtrack(state, choices, selected, res)
selected[i] = False
state.pop()
def permutations_ii(nums: list[int]) -> list[list[int]]:
res = []
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
return res
Comparison of Pruning Methods
- Repeated Choice Pruning: Prevents an element from appearing more than once in a permutation using a single
selectedarray throughout the search. - Equal Element Pruning: Ensures that duplicate elements are selected only once per recursive call level using a
duplicatedset in each recursion.